/*
  Copyright (c) 2002, 2003 Russell Morris
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, 
  are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, 
      this list of conditions and the following disclaimer. 
      
    * Redistributions in binary form must reproduce the above copyright notice, 
      this list of conditions and the following disclaimer in the documentation 
      and/or other materials provided with the distribution. 
      
    * The names of the contributors may not be used to endorse or promote 
      products derived from this software without specific prior written permission. 
      
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
    THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
using System.Windows.Forms;
//using PropertyTree.Design;

namespace WRM.Windows.Forms
{
    /// <summary>
    ///   Indices to auto-navigate node images
    /// </summary>
    public enum AutoNavigateImage
    {
        /// <summary>
        ///   Unselected leaf node
        /// </summary>
        UnselLeaf = 1,
        /// <summary>
        ///   Selected leaf node
        /// </summary>
        SelLeaf = 0,
        /// <summary>
        ///   Closed parent node
        /// </summary>
        ClosedFolder = 3,
        /// <summary>
        ///   Open parent node
        /// </summary>
        OpenFolder = 2
    }
  
    /// <summary>
    ///   A class that houses sets of controls like a <see cref="TabControl"/>.  Each set
    /// of controls is called a <see cref="PropertyPane"/>.  <see cref="PropertyPane"/>s 
    /// are arranged in a <see cref="TreeView"/> - clicking on a node in the <c>TreeView</c>
    /// makes its associated <see cref="PropertyPane"/> displayed.
    /// </summary>
    [Designer(typeof(WRM.Windows.Forms.Design.PropertyTreeDesigner))]
    [DesignerSerializer(typeof(WRM.Windows.Forms.Design.PaneNodeCollectionSerializer),
         typeof(CodeDomSerializer))]
    [DesignTimeVisible(true)]
    [DefaultEvent("PaneDeactivating")]
    public class PropertyTree : UserControl, ISupportInitialize
    {
        /// <summary>
        ///   Our TreeView
        /// </summary>
        private System.Windows.Forms.TreeView tvTree;

        /// <summary>
        ///   The label that plays host to all <see cref="PropertyPane"/>s
        /// </summary>
        private System.Windows.Forms.Panel lblPlacement;

        /// <summary>
        /// Automatically added by Windows.Forms designer
        /// </summary>
        private System.ComponentModel.IContainer components;

        /// <summary>
        ///   Our dictionary mapping <see cref="PropertyPane"/>s to TreeNodes
        /// </summary>
        private Hashtable mPaneNodeDic;

        /// <summary>
        ///   Our shared pane instances - 1 per Type
        /// </summary>
        internal System.Collections.Hashtable mSharedPanesDic;

        /// <summary>
        ///   Event delegate for PaneDeactivating
        /// </summary>
        /// <param name="sender">
        /// The sender of this event
        /// </param>
        /// <param name="psea">
        /// Information about which <see cref="PaneNode"/>s are involved
        /// of this event
        /// </param>
        public delegate void PaneDeactivatingEventHandler(PropertyTree sender, 
            PaneSelectionEventArgs psea);
    
        /// <summary>
        ///   Event delegate for PaneDeactivated
        /// </summary>
        /// <param name="sender">
        /// The sender of this event
        /// </param>
        /// <param name="psea">
        /// Information about which <see cref="PaneNode"/>s are involved
        /// of this event
        /// </param>
        public delegate void PaneDeactivatedEventHandler(PropertyTree sender,
            PaneSelectionEventArgs psea);

        /// <summary>
        ///   Event handler for PaneActivating
        /// </summary>
        /// <param name="sender">
        /// The sender of this event
        /// </param>
        /// <param name="psea">
        /// Information about which <see cref="PaneNode"/>s are involved
        /// of this event
        /// </param>
        public delegate void PaneActivatingEventHandler(PropertyTree sender,
            PaneSelectionEventArgs psea);

        /// <summary>
        ///   Event handler for PaneActivated
        /// </summary>
        /// <param name="sender">
        /// The sender of this event
        /// </param>
        /// <param name="psea">
        /// Information about which <see cref="PaneNode"/>s are involved
        /// of this event
        /// </param>
        public delegate void PaneActivatedEventHandler(PropertyTree sender,
            PaneSelectionEventArgs psea);

        /// <summary>
        ///   The delegate for PaneDeactivating
        /// </summary>
        private PaneDeactivatingEventHandler mOnPaneDeactivating;

        /// <summary>
        ///   The delegate for PaneDeactivated
        /// </summary>
        private PaneDeactivatedEventHandler mOnPaneDeactivated;

        /// <summary>
        ///   The delegate for PaneActivating
        /// </summary>
        private PaneActivatingEventHandler mOnPaneActivating;

        /// <summary>
        ///   The delegate for PaneActivated
        /// </summary>
        private PaneActivatedEventHandler mOnPaneActivated;
    
        /// <summary>
        ///   An internal variable that is set to true when we need
        /// to change selection without raising selection-change events
        /// </summary>
        internal bool mInternalSelChange = false;

        /// <summary>
        ///  This is set when removing a shared pane
        /// </summary>
        internal bool mRemovingSharedPane = false;

        /// <summary>
        ///  Our root PaneNode object
        /// </summary>
        internal PaneNode mRootPaneNode;
    
        /// <summary>
        ///  The imagelist that contains the AutoNavigate icons
        /// </summary>
        private System.Windows.Forms.ImageList ilTree;
    
        /// <summary>
        ///   The splitter
        /// </summary>
        private System.Windows.Forms.Splitter splitter;

        /// <summary>
        ///   Whether or not we are in AutoNavigate mode
        /// </summary>
        private bool mAutoNavigate;

        /// <summary>
        ///   The header control shown above the active 
        ///   <see cref="PropertyPane">PropertyPane</see>.
        /// </summary>
        private PaneHeader paneTitleLabel;

        /// <summary>
        ///   Boolean value indicating whether or not we are being initialized
        ///   by designer-generated code
        /// </summary>
        private bool mInit;

        /// <summary>
        ///   Collection of <see cref="PropertyPane"/>s that were added by designer-generated
        ///   code during initialization.
        /// </summary>
        private ArrayList mAddedDuringInit;

        /// <summary>
        ///   Our default constructor
        /// </summary>
        public PropertyTree()
        {
            mPaneNodeDic = new Hashtable(10);
            mSharedPanesDic = new Hashtable(10);
            mRootPaneNode = new PaneNode(this);
            mAddedDuringInit = new ArrayList();
      
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();
        }

        /// <summary>
        ///   Gets the <see cref="PaneNodeCollection"/> that holds all 
        ///   <see cref="PaneNode"/> instances that are children of the
        ///   root of the <c>PropertyTree</c>.
        /// </summary>
        /// <value>
        /// A reference to the root <see cref="PaneNode"/>'s 
        /// <see cref="PaneNode.PaneNodes">PaneNodes</see> collection.
        /// </value>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public PaneNodeCollection PaneNodes
        {
            get
            {
                return mRootPaneNode.PaneNodes;
            }
        }

        /// <summary>
        /// Gets the <see cref="Control.ControlCollection"/> that represents the child
        /// controls of this control.
        /// </summary>
        /// <value>
        /// A reference to <see cref="Control.Controls"/>.
        /// </value>
        /// <remarks>
        /// This property is hijacked (via the <c>new</c> keyword) so that we can
        /// force the Windows.Forms designer to not serialize this collection.
        /// Representation of the controls in a <c>PropertyTree</c> is handled by
        /// <see cref="PropertyTree.PaneNodes"/>.
        /// </remarks>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new Control.ControlCollection Controls
        {
            get
            {
                return base.Controls;
            }
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">
        /// <c>true</c> to release both managed and unmanaged resources; 
        /// <c>false</c> to release only unmanaged resources.
        /// </param>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if( components != null )
                    components.Dispose();
            }
            base.Dispose( disposing );
        }

#region Component Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(PropertyTree));
            this.lblPlacement = new System.Windows.Forms.Panel();
            this.paneTitleLabel = new WRM.Windows.Forms.PaneHeader();
            this.tvTree = new System.Windows.Forms.TreeView();
            this.ilTree = new System.Windows.Forms.ImageList(this.components);
            this.splitter = new System.Windows.Forms.Splitter();
            this.lblPlacement.SuspendLayout();
            this.SuspendLayout();
            // 
            // lblPlacement
            // 
            this.lblPlacement.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                                       this.paneTitleLabel});
            this.lblPlacement.Dock = System.Windows.Forms.DockStyle.Fill;
            this.lblPlacement.Location = new System.Drawing.Point(188, 0);
            this.lblPlacement.Name = "lblPlacement";
            this.lblPlacement.Size = new System.Drawing.Size(436, 236);
            this.lblPlacement.TabIndex = 2;
            // 
            // paneTitleLabel
            // 
            this.paneTitleLabel.Dock = System.Windows.Forms.DockStyle.Top;
            this.paneTitleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
            this.paneTitleLabel.ForeColor = System.Drawing.SystemColors.ActiveCaptionText;
            this.paneTitleLabel.LeftColor = System.Drawing.SystemColors.ActiveCaption;
            this.paneTitleLabel.Name = "paneTitleLabel";
            this.paneTitleLabel.RightColor = System.Drawing.SystemColors.Control;
            this.paneTitleLabel.Size = new System.Drawing.Size(436, 20);
            this.paneTitleLabel.TabIndex = 2;
            // 
            // tvTree
            // 
            this.tvTree.Dock = System.Windows.Forms.DockStyle.Left;
            this.tvTree.HideSelection = false;
            this.tvTree.ImageIndex = -1;
            this.tvTree.Name = "tvTree";
            this.tvTree.SelectedImageIndex = -1;
            this.tvTree.Size = new System.Drawing.Size(184, 236);
            this.tvTree.TabIndex = 0;
            this.tvTree.AfterExpand += new System.Windows.Forms.TreeViewEventHandler(this.tvTree_AfterExpand);
            this.tvTree.AfterCollapse += new System.Windows.Forms.TreeViewEventHandler(this.tvTree_AfterCollapse);
            this.tvTree.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvTree_BeforeSelect);
            // 
            // ilTree
            // 
            this.ilTree.ColorDepth = System.Windows.Forms.ColorDepth.Depth24Bit;
            this.ilTree.ImageSize = new System.Drawing.Size(16, 16);
            this.ilTree.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("ilTree.ImageStream")));
            this.ilTree.TransparentColor = System.Drawing.Color.Transparent;
            // 
            // splitter
            // 
            this.splitter.BackColor = System.Drawing.SystemColors.AppWorkspace;
            this.splitter.Location = new System.Drawing.Point(184, 0);
            this.splitter.Name = "splitter";
            this.splitter.Size = new System.Drawing.Size(4, 236);
            this.splitter.TabIndex = 1;
            this.splitter.TabStop = false;
            this.splitter.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.splitter_SplitterMoved);
            // 
            // PropertyTree
            // 
            this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                          this.lblPlacement,
                                                                          this.splitter,
                                                                          this.tvTree});
            this.Name = "PropertyTree";
            this.Size = new System.Drawing.Size(624, 236);
            this.lblPlacement.ResumeLayout(false);
            this.ResumeLayout(false);

        }
#endregion

        /// <summary>
        ///   When the user selects a TreeNode, we need to fire our selection-change
        /// to notify people and see if they want to cancel the selection.
        /// </summary>
        /// <param name="sender">The sender of this event</param>
        /// <param name="e">Event details</param>
        private void tvTree_BeforeSelect(object sender, System.Windows.Forms.TreeViewCancelEventArgs e)
        {
            PaneNode paneNode = null;

            // We're doing it internally, so ignore this event
            if(mInternalSelChange)
            {
                e.Cancel = false;
                return;
            }

            paneNode = (PaneNode)mPaneNodeDic[e.Node];
  
            // Initiate the pane selection procedure
            e.Cancel = DoSelectPane(paneNode);

            // Collapse all other branches that are not us if we are
            // in AutoNavigate mode.
            if(e.Cancel == false && mAutoNavigate)
            {
                CollapseSiblingBranches(e.Node);
            }
        }
    
        /// <summary>
        ///   Select a pane given it's <see cref="TreeNode"/>
        /// </summary>
        /// <param name="n">The <see cref="TreeNode"/> to select</param>
        internal void SelectPaneByNode(TreeNode n) 
        {
            tvTree.SelectedNode = n;
        }
    
  
        /// <summary>
        ///   Gets or sets the <see cref="PaneNode"/> that is currently selected (if any)
        /// </summary>
        /// <remarks>
        ///     <para>
        ///     While its use as a getter is obvious, <c>SelectedPaneNode</c>is also the 
        ///     primary means by which selection is programmatically set.
        ///     </para>
        ///     <para>
        ///     To remove selection (ie to make nothing selected), set this 
        ///     property to <c>null</c>.
        ///     </para>
        /// </remarks>
        /// <value>
        /// The value of the property is the <see cref="PaneNode"/> that is currently
        /// selected, or <c>null</c> if none is selected.
        /// </value>
        [Browsable(false)]
        public PaneNode SelectedPaneNode
        {
            get
            {
                TreeNode n = tvTree.SelectedNode;
                if(n == null)
                    return null;
  
                PaneNode paneNode = (PaneNode)mPaneNodeDic[n];
                return paneNode;
            }
            set
            {
                PaneNode paneNode = value;

                //Initiate the Pane selection change
                bool stop = DoSelectPane(paneNode);

                if(!stop)
                {
                    //Set the mInternalSelChange, so that we don't get a stack
                    //overflow
                    mInternalSelChange = true;
                    if(value != null)
                        tvTree.SelectedNode = value.TreeNode;
                    else
                        tvTree.SelectedNode = null;

                    mInternalSelChange = false;
                }
            }
        }

        /// <summary>
        ///   Gets or sets the default image index for all <see cref="PropertyPane"/>s
        /// </summary>
        /// <value>
        /// The integer index into <see cref="PropertyTree.ImageList"/> of the 
        /// image that will be displayed by default for all 
        /// <see cref="PaneNode"/>s.
        /// </value>
        [Description("The default image index for nodes"),
        DefaultValue(-1),
        Category("Behavior")]
        public int ImageIndex
        {
            get
            {
                return tvTree.ImageIndex;
            }
            set
            {
                tvTree.ImageIndex = value;
            }
        }

        /// <summary>
        ///   Gets or sets the indentation of child nodes in pixels
        /// </summary>
        /// <value>
        /// Represents the number of pixels that each child node will be
        /// horizontally offset from its parent.
        /// </value>
        [Description("The indentation of child nodes in pixels"),
        Category("Behavior")]
        public int Indent
        {
            get
            {
                return tvTree.Indent;
            }
            set
            {
                tvTree.Indent = value;
            }
        }

        /// <summary>
        ///   Gets or sets the default image index for all 
        ///   selected <see cref="PropertyPane"/>s
        /// </summary>
        /// <value>
        /// The integer index into <see cref="PropertyTree.ImageList"/> of the 
        /// image that will be displayed by default for all 
        /// <see cref="PaneNode"/>s when they are selected.
        /// </value>
        [Description("The default image index for selected nodes"),
        DefaultValue(0),
        Category("Behavior")]
        public int SelectedImageIndex
        {
            get
            {
                return tvTree.SelectedImageIndex;
            }
            set
            {
                tvTree.SelectedImageIndex = value;
            }
        }

        /// <summary>
        ///   Gets or sets the <see cref="ImageList"/> from which images are taken.  
        ///   This can be set by the user, but its contents are automatically set 
        ///   when <see cref="PropertyTree.AutoNavigate">AutoNavigate</see> == <c>true</c>.
        /// </summary>
        /// <value>
        /// The <see cref="ImageList"/> indexed by the <see cref="PropertyTree.ImageIndex"/> and
        /// <see cref="PropertyTree.SelectedImageIndex"/> properties.
        /// </value>
        [Description("The ImageList control from which images are taken"),
        Category("Behavior")]
        public ImageList ImageList
        {
            get
            {
                return tvTree.ImageList;
            }
            set
            {
                tvTree.ImageList = value;
            }
        }
  
        /// <summary>
        /// Gets or sets a boolean value that indicates whether or not
        /// lines are displayed between related nodes.
        /// </summary>
        /// <value>
        /// <c>true</c> indicates that lines will be drawn between
        /// related nodes, <c>false</c> indicates that no lines
        /// will be drawn.
        /// </value>
        [Description("Indicates whether lines are displayed " +
             "between sibling nodes and between parent " +
             "and child nodes"),
        Category("Behavior")]
        public bool ShowLines
        {
            get
            {
                return tvTree.ShowLines;
            }
            set
            {
                tvTree.ShowLines = value;
            }
        }

        /// <summary>
        ///   Gets or sets a boolean value that indicates whether or not
        ///   the plus/minus glyph is displayed for node that has
        ///   child nodes.
        /// </summary>
        /// <value>
        /// <c>true</c> indicates that the plus/minus glyphs will be
        /// drawn, <c>false</c> indicates they will not be drawn.
        /// </value>
        [Description("Indicates whether a plush/minus buttons are " +
             "shown next to parent nodes"),
        Category("Behavior")]
        public bool ShowPlusMinus
        {
            get
            {
                return tvTree.ShowPlusMinus;
            }
            set
            {
                tvTree.ShowPlusMinus = value;
            }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether or not lines are drawn between
        /// root nodes of the tree.
        /// </summary>
        /// <value>
        /// <c>true</c> indicates that lines will be drawn between root nodes, 
        /// <c>false</c> indicates that lines will not be drawn.
        /// </value>
        /// <remarks>
        /// This value must be <c>true</c> and <see cref="ShowPlusMinus"/> must be
        /// <c>true</c> in order for the plus/minus buttons to be drawn for root nodes.
        /// </remarks>
        [Description("Indicates whether lines are displayed between root nodes"),
        Category("Behavior")]
        public bool ShowRootLines
        {
            get
            {
                return tvTree.ShowRootLines;
            }
            set
            {
                tvTree.ShowRootLines = value;
            }
        }

        /// <summary>
        ///   Initiate the selection change.  
        /// </summary>
        /// <remarks>
        ///   The currently selected <see cref="PaneNode"/> is given a chance to 
        /// veto deactivation, and the new <see cref="PaneNode"/> a chance to veto 
        /// selection.
        ///   Also, if <see cref="PropertyTree.AutoNavigate"/><c> == true</c>, we 
        ///   automatically select the first
        /// child pane that is a leaf node (if this node has children).  As a 
        /// direct result of this, no pane with children can be selected if
        /// <see cref="PropertyTree.AutoNavigate"/><c> == true</c>.
        /// </remarks>
        /// <returns>
        /// Returns <c>true</c> if the selection process is vetoed, 
        /// and <c>false</c> if everything goes ok.
        /// </returns>
        /// <param name="newPaneNode">
        /// The <c>PaneNode</c> representing the <see cref="PropertyPane"/> to be selected
        /// </param>
        protected bool DoSelectPane(PaneNode newPaneNode)
        {
            PaneNode curPaneNode;
            PaneSelectionEventArgs psea;
            bool isSamePaneInstance = false;
      
            // If we're autonavigating, we need to make sure we always select
            // a leaf node
            // 
            if(mAutoNavigate && newPaneNode != null && newPaneNode.TreeNode.Nodes.Count > 0)
            {
                newPaneNode = FindFirstLeafChild(newPaneNode.TreeNode);
                SelectPaneByNode(newPaneNode.TreeNode);
                return true;
            }
      
            if(SelectedPaneNode != null)
                curPaneNode = SelectedPaneNode;
            else
                curPaneNode = null;

            psea = new PaneSelectionEventArgs(curPaneNode,newPaneNode);
      
            // If curPaneNode and newPaneNode use the same PropertyPane instance, then 
            // remember that so we don't call Hide() and Show()
            if(curPaneNode != null && newPaneNode != null)
                isSamePaneInstance = (curPaneNode.PropertyPane == newPaneNode.PropertyPane);

            // Ask the currently selected pane if it can be deactivated.  We first fire
            // the PropertyTree's OnPaneActivating event, and then call 
            // curPaneNode.OnpaneDeactivating() so that custom PropertyPane-derived classes
            // can handle their own OnPaneActivating events instead of relying on
            // whoever is listening to the PropertyTree's events.
            OnPaneDeactivating(psea);
            if(curPaneNode != null)
                curPaneNode.PropertyPane.OnPaneDeactivating(this,psea);

            // If at any point someone set psea.Cancel = true, we back out of the
            // selection processes without changing anything.
            if(psea.Cancel == true)
                return true;

            // Ask the pane about to be selected if it can be selected.  We first fire 
            // the PropertyTree's OnPaneActivating event, and then call 
            // newPaneNode.OnPaneActivating() so that custom PropertyPane-derived classes
            // can handle their own OnPaneActivating events instead of relying on
            // whoever is listening to the PropertyTree's events.
            OnPaneActivating(psea);
            if(newPaneNode != null)
                newPaneNode.PropertyPane.OnPaneActivating(this,psea);
        
            // If at any point someone set psea.Cancel to true, we back out of the
            // selection process without making any changes.
            if(psea.Cancel == true)
                return true;

            // Set the mInternalSelChange, so that we don't get a stack
            // overflow when forcing the node to be selected.
            mInternalSelChange = true;
            if(newPaneNode != null)
                tvTree.SelectedNode = newPaneNode.TreeNode;
            else
                tvTree.SelectedNode = null;
            mInternalSelChange = false;

            //Now we actually deactivate (ie hide) the currently selected
            //pane, if there is one.  We make sure not to do this if isSameInstance is
            //true.
            if(curPaneNode != null && !isSamePaneInstance)
                curPaneNode.PropertyPane.Hide();

            // Notify the current pane that it has been deactivated
            OnPaneDeactivated(psea);
            if(curPaneNode != null)
                curPaneNode.PropertyPane.OnPaneDeactivated(this,psea);

            //Activate the new pane
            if(newPaneNode != null && !isSamePaneInstance)
                ShowPaneNode(newPaneNode);

            if(newPaneNode != null)
            {
                // If newPaneNode is shared, we need to fire its OnPaneInstanceChanged event.
                if(newPaneNode.IsShared)
                {
                    SharedPropertyPane sharedPane = newPaneNode.PropertyPane as SharedPropertyPane;
                    sharedPane.OnPaneInstanceChanged(newPaneNode);
                }

                newPaneNode.PropertyPane.OnPaneActivated(this,psea);

                paneTitleLabel.Text = newPaneNode.Text;
            }
            else
                paneTitleLabel.Text = "";

            //Fire the Activated event
            OnPaneActivated(psea);
        
            // Return false to indicate that everything went OK
            return false;
        }

        /// <summary>
        ///   Recursively finds the first leaf child for the given node n.
        /// </summary>
        /// <param name="n">The parent node</param>
        /// <returns>
        /// Returns a <see cref="PaneNode"/> identifying the first child node that 
        /// is a leaf.
        /// </returns>
        internal PaneNode FindFirstLeafChild(TreeNode n)
        {
            PaneNode paneNode;
            foreach(TreeNode child in n.Nodes)
            {
                if(child.Nodes.Count == 0)
                {
                    paneNode = (PaneNode)mPaneNodeDic[child];
                    return paneNode;
                }
                else
                    return FindFirstLeafChild(child);
            }

            paneNode = (PaneNode)mPaneNodeDic[n];
            return paneNode;
        }
    
        /// <summary>
        ///   Make a <see cref="PaneNode"/> visible
        /// </summary>
        /// <param name="paneNode">
        /// The <c>PaneNode</c> to make visible</param>
        protected void ShowPaneNode(PaneNode paneNode)
        {
            if(paneNode != null)
            {
                // Insert the pane if we haven't already...
                if(!Controls.Contains(paneNode.PropertyPane))
                    Controls.Add(paneNode.PropertyPane);

                paneNode.PropertyPane.Show();
                paneNode.PropertyPane.BringToFront();
            }
        }

        /// <summary>
        ///   Event fired before a pane is activated.
        /// </summary>
        [Category("Pane Selection")]
        [Description("Fired before a pane is activated")]
        public event PaneActivatingEventHandler PaneActivating
        {
            remove
            {
                mOnPaneActivating -= value;
            }
            add
            {
                mOnPaneActivating = value;
            }
        }
  
        /// <summary>
        ///   Event fired after a pane is activated
        /// </summary>
        [Category("Pane Selection")]
        [Description("Fired after a pane is activated")]
        public event PaneActivatedEventHandler PaneActivated
        {
            remove
            {
                mOnPaneActivated -= value;
            }
            add
            {
                mOnPaneActivated = value;
            }
        }
  
        /// <summary>
        ///   Event fired before a pane is deactivated
        /// </summary>
        [Category("Pane Selection")]
        [Description("Fired before a pane is deactivated")]
        public event PaneDeactivatingEventHandler PaneDeactivating
        {
            remove
            {
                mOnPaneDeactivating -= value;
            }
            add
            {
                mOnPaneDeactivating = value;
            }
        }
  
        /// <summary>
        ///   Event fired after a pane is deactivated
        /// </summary>
        [Category("Pane Selection")]
        [Description("Fired after a pane is deactivated")]
        public event PaneDeactivatedEventHandler PaneDeactivated
        {
            remove
            {
                mOnPaneDeactivated -= value;
            }
            add
            {
                mOnPaneDeactivated = value;
            }
        }
  
        /// <summary>
        ///   Method to fire the <see cref="PropertyTree.PaneActivating">
        ///   PaneActivating</see> event
        /// </summary>
        /// <param name="psea">
        /// Information about the event
        /// </param>
        protected virtual void OnPaneActivating(PaneSelectionEventArgs psea)
        {
            if(mOnPaneActivating != null)
                mOnPaneActivating(this,psea);
        }

        /// <summary>
        ///   Method to fire the <see cref="PropertyTree.PaneActivated">
        ///   PaneActivated</see> event
        /// </summary>
        /// <param name="psea">Information about the event</param>
        protected virtual void OnPaneActivated(PaneSelectionEventArgs psea)
        {
            if(mOnPaneActivated != null)
                mOnPaneActivated(this,psea);
        }

        /// <summary>
        ///   Method to fire the <see cref="PropertyTree.PaneDeactivating">
        ///   PaneDeactivating</see> event
        /// </summary>
        /// <param name="psea">Information about the event</param>
        protected virtual void OnPaneDeactivating(PaneSelectionEventArgs psea)
        {
            if(mOnPaneDeactivating != null)
                mOnPaneDeactivating(this,psea);
        }

        /// <summary>
        ///   Method to fire the <see cref="PropertyTree.PaneDeactivated">
        ///   PaneDeactivated</see> event
        /// </summary>
        /// <param name="psea">Information about the event</param>
        protected virtual void OnPaneDeactivated(PaneSelectionEventArgs psea)
        {
            if(mOnPaneDeactivated != null)
                mOnPaneDeactivated(this,psea);
        }

        /// <summary>
        ///   After a node is expanded, tell the <see cref="PaneNode"/> about it.
        /// </summary>
        /// <remarks>
        ///   Notify the <see cref="PaneNode"/> of this event so that it can keep its
        /// <see cref="PaneNode.Expanded"/> property up to date
        /// </remarks>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">Information about the event</param>
        private void tvTree_AfterExpand(object sender, System.Windows.Forms.TreeViewEventArgs e)
        {
            PaneNode node = mPaneNodeDic[e.Node] as PaneNode;

            // Tell the PropertyPane it has been expanded.
            // 
            if(node != null)
                node.ExpandedInternal = true;

            // If we're AutoNavigating, update this node's ImageIndex to the open folder
            if(mAutoNavigate && e.Node.Nodes.Count > 0)
            {
                e.Node.ImageIndex = (int)AutoNavigateImage.OpenFolder;
            }
        }

        /// <summary>
        ///   After a node is collapsed, tell the <see cref="PaneNode"/> about it.
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">Information about the event</param>
        /// <remarks>
        ///   Notify the <see cref="PaneNode"/> about this event so that it
        ///   can keep its <see cref="PaneNode.Expanded"/> property up to date
        /// </remarks>
        private void tvTree_AfterCollapse(object sender, System.Windows.Forms.TreeViewEventArgs e)
        {
            PaneNode node = mPaneNodeDic[e.Node] as PaneNode;

            //  Tell the PropertyPane it has been collapsed
            //  
            node.ExpandedInternal = false;

            // If we're AUtoNavigating, update this node's ImageIndex to the closed folder
            if(mAutoNavigate && e.Node.Nodes.Count > 0)
            {
                e.Node.ImageIndex = (int)AutoNavigateImage.ClosedFolder;
            }
        }

        /// <summary>
        ///   Gets the <see cref="TreeView"/> that holds the <see cref="PaneNode"/>s 
        ///   for this <c>PropertyTree</c>
        /// </summary>
        /// <value>
        ///   The <see cref="TreeView"/> that holds the <see cref="PaneNode"/>s for
        ///   this <c>PropertyTree</c>
        /// </value>
        /// <remarks>
        ///   This is internally visible so that the custom designer objects may reference
        /// it when they need to.
        /// </remarks>
        /// <seealso cref="WRM.Windows.Forms.Design.PropertyTreeDesigner"/>
        internal TreeView TreeView
        {
            get
            {
                return tvTree;
            }
        }

        /// <summary>
        ///   Gets a boolean value indicating whether or not we're in 
        ///   AutoNavigate mode.  This mode is like the <see cref="PropertyTree"/> 
        ///   in the VS.NET IDE's Tools | Options dialog.  Only leaf
        ///   nodes may be selected.
        /// </summary>
        /// <value>
        /// <c>true</c> indicates AutoNavigate mode is turned on, <c>false</c>
        /// indicates that it is turned off
        /// </value>
        [Description("Ensure that only leaf nodes can be selected.")]
        [Category("Behavior")]
        [DefaultValue(false)]
        public bool AutoNavigate
        {
            get
            {
                return mAutoNavigate;
            }
            set
            {
                mAutoNavigate = value;
        
                if(value == false)
                {
                    ImageList = null;
                    UpdateNodesImages();
                }
                else
                {
                    if(SelectedPaneNode != null)
                        CollapseSiblingBranches(SelectedPaneNode.TreeNode);

                    ImageList = ilTree;
                    UpdateNodesImages();

                    ShowLines = false;
                    ShowPlusMinus = false;
                    ShowRootLines = false;
                }
            }
        }

        /// <summary>
        ///   Update each node's image and sel-image indices.
        /// </summary>
        internal void UpdateNodesImages()
        {
            if(!mAutoNavigate)
                return;

            foreach(TreeNode n in tvTree.Nodes)
            {
                UpdateNodeImages(n);
                UpdateChildNodesImages(n);
            }
        }

        /// <summary>
        ///   Update the Child nodes images
        /// </summary>
        /// <param name="n">
        /// The <c>TreeNode</c> whose images need updating
        /// </param>
        internal void UpdateChildNodesImages(TreeNode n)
        {
            foreach(TreeNode child in n.Nodes)
            {
                UpdateNodeImages(child);
                UpdateChildNodesImages(child);
            }
        }

        /// <summary>
        ///   Update this <see cref="TreeNode"/>'s 
        ///   <see cref="TreeNode.ImageIndex"/> and 
        ///   <see cref="TreeNode.SelectedImageIndex"/>
        /// </summary>
        /// <param name="n">
        ///     The <c>TreeNode</c> to update
        /// </param>
        internal void UpdateNodeImages(TreeNode n)
        {
            if(!mAutoNavigate)
            {
                // If we're not autonavigating, we nix all image indices
                n.ImageIndex = -1;
                n.SelectedImageIndex = -1;
                return;
            }
      
            if(n.Nodes.Count == 0)
            {
                // We're a leaf node - we show nothing or the gray arrow
                // 
                n.ImageIndex = (int)AutoNavigateImage.UnselLeaf;
                n.SelectedImageIndex = (int)AutoNavigateImage.SelLeaf;
            }
            else
            {
                if(n.IsExpanded)
                {
                    n.ImageIndex = (int)AutoNavigateImage.OpenFolder;
                    n.SelectedImageIndex = (int)AutoNavigateImage.OpenFolder;
                }
                else
                {
                    n.ImageIndex = (int)AutoNavigateImage.ClosedFolder;
                    n.SelectedImageIndex = (int)AutoNavigateImage.ClosedFolder;
                }
            }
        }

        /// <summary>
        ///   Collapse all sibling branches of n.  This is used during AutoNavigate
        /// selection changes.
        /// </summary>
        /// <param name="n">
        /// The <c>TreeNode</c> to for which to collapse sibling branches
        /// </param>
        internal void CollapseSiblingBranches(TreeNode n)
        {
            TreeNodeCollection parentNodes;

            if(n.Parent == null)
            {
                parentNodes = tvTree.Nodes;
            }
            else
            {
                parentNodes = n.Parent.Nodes;
            }

            foreach(TreeNode sibling in parentNodes)
            {
                // Collapse the node, if it has children
                // 
                if(sibling != n && sibling.Nodes.Count > 0)
                {
                    sibling.Collapse();
                }
            }

            // And walk all the way to the top
            if(n.Parent != null)
                CollapseSiblingBranches(n.Parent);
        }

        /// <summary>
        /// Handle the Resize event
        /// </summary>
        /// <param name="e">Information about the event</param>
        /// <remarks>
        /// This event is handled so that the currently active
        /// <see cref="PropertyPane"/> can be resized accordingly.
        /// </remarks>
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            AdjustPaneArea ();
        }
    
        /// <summary>
        ///   Resize the panes and the title label when the 
        ///   <see cref="PropertyTree"/> changes size.
        /// </summary>
        /// <param name="p">
        /// The <see cref="PropertyPane"/> to resize
        /// </param>
        protected void ResizePane(PropertyPane p)
        {
            // Resize each of our panes manually, since it doesn't seem that we can get accurate
            // resizing by relying on Anchor styles
            //
            Point topLeft = lblPlacement.Location;
            topLeft.Y += paneTitleLabel.Bottom + 1;
            Size size = new Size(0,0);
            size.Width = lblPlacement.Width;
            size.Height = Height - paneTitleLabel.Bottom - 1;
            p.Location = topLeft;
            p.Size = size;
        }
    
        /// <summary>
        ///   Override handling of the <c>OnControlAdded</c> event to 
        ///   do some setup work.
        /// </summary>
        /// <param name="e">
        /// Information about this event
        /// </param>
        /// <remarks>
        /// When <see cref="PropertyPane"/>s start getting added, they need to 
        /// know who their owner is, and that they are sized correctly.
        /// </remarks>
        protected override void OnControlAdded(System.Windows.Forms.ControlEventArgs e)
        {
            base.OnControlAdded(e);
        
            if(e.Control is PropertyPane)
            {
                PropertyPane pane = (PropertyPane)e.Control;
                pane.Owner = this;
                ResizePane(pane);
            }
        }

        /// <summary>
        ///   Handle splitter movement by resizing things
        /// </summary>
        /// <param name="sender">The sender of this event</param>
        /// <param name="e">Information about the event</param>
        private void splitter_SplitterMoved(object sender, System.Windows.Forms.SplitterEventArgs e)
        {
            AdjustPaneArea();
        }

        /// <summary>
        /// This utility method is called whenever the splitter has been moved,
        /// or when the <see cref="PropertyTree"/> has been resized
        /// </summary>
        private void AdjustPaneArea()
        {
            lblPlacement.Left = splitter.Right + 1;
            lblPlacement.Width = Width - splitter.Right -1;
      
            foreach(Control c in Controls)
            {
                if(c is PropertyPane)
                    ResizePane((PropertyPane)c);
            }
        }

    #region Shared Panes machinery
        /// <summary>
        /// Struct to hold information about current instances of
        /// <see cref="SharedPropertyPane"/> instances.
        /// </summary>
        private struct SharedPaneInstance
        {
            /// <summary>
            /// The instance of a <see cref="SharedPropertyPane"/>-derived class
            /// </summary>
            public SharedPropertyPane Instance;
            
            /// <summary>
            /// The number of <see cref="PaneNode"/>s that reference it
            /// </summary>
            public int RefCount;

            /// <summary>
            /// Create an instance of <c>SharedPaneInstace</c>, with the
            /// given <paramref name="instance"/> of a 
            /// <see cref="SharedPropertyPane"/>-derived class.
            /// </summary>
            /// <param name="instance">
            /// An instance of a <c>SharedPropertyPane</c>-derived class
            /// </param>
            public SharedPaneInstance(SharedPropertyPane instance)
            {
                Instance = instance;
                RefCount = 0;
            }
        }
    #endregion
    
        /// <summary>
        /// Returns an instance of the <see cref="SharedPropertyPane"/>-derived type
        /// <paramref name="sharedPaneType"/>
        /// </summary>
        /// <param name="sharedPaneType">
        /// A <see cref="System.Type"/> object representing the 
        /// <see cref="SharedPropertyPane"/>-derived class to create or get
        /// an instance of
        /// </param>
        /// <returns>
        /// Returns an instance of the type <paramref name="sharedPaneType"/>
        /// </returns>
        /// <remarks>
        /// Only 1 instance of <paramref name="sharedPaneType"/> will ever be
        /// created during the lifetime of this <see cref="PropertyTree"/>
        /// </remarks>
        internal SharedPropertyPane GetSharedPaneInstance(Type sharedPaneType)
        {
            SharedPaneInstance spi;

            if(!mSharedPanesDic.Contains(sharedPaneType))
            {
                SharedPropertyPane spp = (SharedPropertyPane)
                    Activator.CreateInstance(sharedPaneType);
                spi = new SharedPaneInstance(spp);
                mSharedPanesDic[sharedPaneType] = spi;
            }
            else
                spi = (SharedPaneInstance)mSharedPanesDic[sharedPaneType];

            spi.RefCount += 1;

            return spi.Instance;
        }

        /// <summary>
        /// Release an instance of the <see cref="SharedPropertyPane"/>-derived
        /// type <paramref name="sharedPaneType"/>
        /// </summary>
        /// <param name="sharedPaneType">
        /// A <see cref="System.Type"/> object representing the 
        /// <see cref="SharedPropertyPane"/>-derived class for which to release
        /// an instance.
        /// </param>
        /// <remarks>
        /// Once the reference count goes to zero, the instance of 
        /// <paramref name="sharedPaneType"/> is removed from the 
        /// <see cref="PropertyTree"/> and disposed.
        /// </remarks>
        internal void ReleaseSharedPaneInstance(Type sharedPaneType)
        {
            SharedPaneInstance spi;

            if(!mSharedPanesDic.Contains(sharedPaneType))
            {
                throw new ArgumentException(
                    "Cannot free SharedPropertyPane type '" + sharedPaneType.FullName + "' " +
                    "because no instances of it are currently in use.");
            }

            spi = (SharedPaneInstance)mSharedPanesDic[sharedPaneType];
            spi.RefCount -= 1;

            if(spi.RefCount == 0)
            {
                Controls.Remove(spi.Instance);
                mSharedPanesDic.Remove(sharedPaneType);
            }
        }

        /// <summary>
        /// Determines whether or not a <see cref="SharedPropertyPane"/> instance 
        /// of type <paramref name="sharedPaneType"/> is currently being cached.
        /// </summary>
        /// <param name="sharedPaneType">
        /// A <see cref="System.Type"/> object representing the 
        /// <see cref="SharedPropertyPane"/>-derived class to check for
        /// </param>
        /// <returns>
        /// Returns a boolean value indicating whether or not an instance of
        /// the type <paramref name="sharedPaneType"/> is currently being
        /// referenced by a <see cref="PaneNode"/>.
        /// </returns>
        internal bool IsSharedPaneInstanceInUse(Type sharedPaneType)
        {
            SharedPaneInstance spi;

            if(!mSharedPanesDic.Contains(sharedPaneType))
                return false;

            spi = (SharedPaneInstance)mSharedPanesDic[sharedPaneType];
      
            return spi.RefCount > 0;
        }

        /// <summary>
        /// Gets a boolean value indicating whether or not we are currently 
        /// initializing.
        /// </summary>
        /// <value>
        /// In conjuction with the <see cref="PropertyTree"/>'s implementation of
        /// <see cref="ISupportInitialize"/>, this value returns the value of an
        /// internal flag that indicates that the <c>PropertyTree</c> is currently
        /// in between calls to <see cref="ISupportInitialize.BeginInit">BeginInit</see>
        /// and <see cref="ISupportInitialize.EndInit">EndInit</see>.
        /// </value>
        internal bool IsInitializing
        {
            get
            {
                return mInit;
            }
        }
    
        /// <summary>
        /// Adds the given <paramref name="paneNode"/> to the tree, using
        /// <paramref name="parent"/> as the parent.
        /// </summary>
        /// <param name="parent">
        /// The <c>PaneNode</c> that will act as the parent of 
        /// <paramref name="paneNode"/>
        /// </param>
        /// <param name="paneNode">
        /// The <c>PaneNode</c> that is to be added to the <c>PropertyTree</c>
        /// </param>
        internal void AddPaneNodeToTree(PaneNode parent, PaneNode paneNode)
        {
            TreeNodeCollection parentCol = null;
            int insertIndex = paneNode.Index;

            if(parent == mRootPaneNode)
                parentCol = tvTree.Nodes;
            else
                parentCol = parent.TreeNode.Nodes;

            // Figure out the insert index - if it's -1, we'll set it to
            // index just past the end of parentCol
            if(insertIndex == -1)
                insertIndex = parentCol.Count;

            TreeNode treeNode = new TreeNode(
                paneNode.Text,
                paneNode.ImageIndex,
                paneNode.SelectedImageIndex);

            // Perform the actual insertion
            parentCol.Insert(paneNode.Index,treeNode);
            paneNode.TreeNode = treeNode;
            mPaneNodeDic[paneNode.TreeNode] = paneNode;

            // If paneNode is a shared node, we need to also set its
            // PropertyPaneInternal property so that it has one to work
            // with.  This cannot be set earlier, because only the
            // PropertyTree is responsible for loading and caching
            // instances of shared panes
            if(paneNode.IsShared)
                paneNode.PropertyPaneInternal = 
                    GetSharedPaneInstance(paneNode.SharedPaneType);

            // Finally, hook up the TabOut handler so we can automatically
            // navigate tab key presses
            paneNode.PropertyPane.TabOut += new PaneTabOutHandler(this.PaneTabOut);
        }


        /// <summary>
        /// Remove <paramref name="paneNode"/> from the <c>PropertyTree</c>
        /// </summary>
        /// <param name="paneNode">
        /// The <c>PaneNode</c> to remove
        /// </param>
        internal void RemovePaneNodeFromTree(PaneNode paneNode)
        {
            mPaneNodeDic.Remove(paneNode.TreeNode);

            if(paneNode.TreeNode.TreeView != null)
                paneNode.TreeNode.Remove();

            paneNode.TreeNode = null;

            paneNode.PropertyPane.TabOut -= new PaneTabOutHandler(this.PaneTabOut);
        }

        /// <summary>
        /// Gets the <see cref="PaneNode"/> that is
        /// located at the coordinates given in <paramref name="pt"/>.
        /// </summary>
        /// <param name="pt">
        /// The coordinates to search for a <see cref="PaneNode"/>
        /// </param>
        /// <returns>
        /// Returns the <see cref="PaneNode"/> located at the
        /// given point <paramref name="pt"/>, or <c>null</c>
        /// if no <c>PaneNode</c> is located there.
        /// </returns>
        public PaneNode GetPaneNodeAt(Point pt)
        {
            PaneNode paneNode;
            TreeNode n;
      
            n = tvTree.GetNodeAt(pt);
      
            if(n == null)
                return null;

            paneNode = mPaneNodeDic[n] as PaneNode;
      
            return paneNode;
        }

    #region Implementation of ISupportInitialize
        /// <summary>
        /// Implementation of <see cref="ISupportInitialize.BeginInit"/>
        /// </summary>
        public void BeginInit()
        {
            mInit = true;
        }

        /// <summary>
        /// Implementation of <see cref="ISupportInitialize.EndInit"/>
        /// </summary>
        public void EndInit()
        {
            mInit = false;
            FlushInitCache();
        }
    #endregion

        /// <summary>
        /// Flush out the cache of added <see cref="PaneNode"/>s that we
        /// stored during initialization.
        /// </summary>
        /// <remarks>   
        /// After <see cref="ISupportInitialize.EndInit"/> is called, 
        /// <c>FlushCache</c> is called to add all of the 
        /// <see cref="PaneNode"/>s cached during initialization to 
        /// the <c>PropertyTree</c>
        /// </remarks>
        private void FlushInitCache()
        {
            foreach(PaneNode child in mRootPaneNode.PaneNodes)
            {
                AddPaneNodeToTree(mRootPaneNode,child);
            }
        }

        /// <summary>
        /// Method to handle the <see cref="PropertyPane.TabOut"/> event
        /// for all of the <see cref="PropertyPane"/>s in the
        /// <c>PropertyTree</c>.
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">Information about the event</param>
        /// <remarks>
        ///     <para>
        ///     This handler uses an algorithm to automatically determine the next
        ///     <see cref="PropertyPane"/> to be given focus, and sets focus to
        ///     it.
        ///     </para>
        ///     <para>
        ///     It starts by getting the next <see cref="PaneNode"/>, using
        ///     in-order tree traversal via <see cref="PropertyTree.GetOrderedPaneNodes"/>,
        ///     and finding the appropriate focusable control on that <see cref="PaneNode"/>'s
        ///     <see cref="PaneNode.PropertyPane">PaneNode.PropertyPane</see> via
        ///     <see cref="GetBoundaryControl"/>.
        ///     </para>
        /// </remarks>
        private void PaneTabOut(object sender, PaneTabOutEventArgs e)
        {
            PaneNode next = GetNextInOrder((PaneNode)sender,e.Forward);

            if(next != null)
            {
                SelectedPaneNode = next;
                next.PropertyPane.Select();
                Control boundaryCtrl = GetBoundaryControl(next.PropertyPane,e.Forward);
                if(boundaryCtrl != null)
                {
                    next.PropertyPane.ActiveControl = boundaryCtrl;
                    e.Success = true;
                }
                else
                    e.Success = false;
            }
            else
                e.Success = false;
        }

        /// <summary>
        /// Find the control that is at one of the extreme ends of the tab order, as
        /// specified by <paramref name="front"/>
        /// </summary>
        /// <param name="p">
        /// The <c>PropertyPane</c> to search for focusable <see cref="Controls"/>
        /// </param>
        /// <param name="front">
        /// Boolean value indicating whether to find the first focusable control 
        /// <see cref="Control"/> at the very front of the tab order or at the very back.
        /// </param>
        /// <returns>
        /// Returns an instance of <see cref="Control"/> that meets the stated criteria,
        /// or returns <c>null</c> if no focusable <c>Control</c>s exist on 
        /// <paramref name="p"/>
        /// </returns>
        private Control GetBoundaryControl(PropertyPane p, bool front)
        {
            Control c = p.ActiveControl;
            Control sel = c;

            while(c != null)
            {
                sel = c;
                c = p.GetNextControl(c,!front);
            }

            return sel;
        }

        /// <summary>
        /// Gets the next <see cref="PaneNode"/> in order, given
        /// <paramref name="curPaneNode"/> as the starting point.
        /// </summary>
        /// <param name="curPaneNode">
        /// The <c>PaneNode</c> from which to start the search
        /// </param>
        /// <param name="forward">
        /// Boolean value indicating whether the search is to proceed forwards
        /// or backwards
        /// </param>
        /// <returns>
        /// Returns an instance of <see cref="PaneNode"/> that represents the
        /// stated criteria, or <c>null</c> if none could be found.
        /// </returns>
        /// <remarks>
        /// The "ordering" is determined by an in-order traversal of the tree.
        /// </remarks>
        private PaneNode GetNextInOrder(PaneNode curPaneNode, bool forward)
        {
            PaneNode[] paneNodes = GetOrderedPaneNodes();
            PaneNode prev = null;
            PaneNode cur = null;
            PaneNode next = null;
            for(int i=0;i<paneNodes.Length;i++)
            {
                prev = cur;
        
                cur = paneNodes[i];
        
                if(i < paneNodes.Length-1)
                    next = paneNodes[i+1];
                else
                    next = null;

                if(cur == curPaneNode)
                    return forward ? next : prev;
            }

            return null;
        }

        /// <summary>
        /// Creates and returns an array of <see cref="PaneNode"/>s that represent an
        /// in-order traversal of the <c>PropertyTree</c>
        /// </summary>
        /// <returns>
        /// Returns an array of <see cref="PaneNode"/>s that represent an
        /// in-order traversal of the <c>PropertyTree</c>
        /// </returns>
        protected virtual PaneNode[] GetOrderedPaneNodes()
        {
            ArrayList orderedList = new ArrayList();
      
            foreach(PaneNode topLevelNode in mRootPaneNode.PaneNodes)
                TraversePaneNode(topLevelNode,orderedList);

            PaneNode[] orderedArray = new PaneNode[orderedList.Count];
            orderedList.CopyTo(orderedArray);

            return orderedArray;
        }

        /// <summary>
        /// Helper function for <see cref="GetOrderedPaneNodes"/>
        /// </summary>
        /// <param name="paneNode">
        /// The <c>PaneNode</c> to inspect
        /// </param>
        /// <param name="orderedList">
        /// The list of <c>PaneNode</c>s that represent the currently generated portion
        /// of the in-order traversal
        /// </param>
        private void TraversePaneNode(PaneNode paneNode, ArrayList orderedList)
        {
            orderedList.Add(paneNode);
      
            foreach(PaneNode child in paneNode.PaneNodes)
                TraversePaneNode(child,orderedList);
        }

        /// <summary>
        /// Gets or sets the color of the left-hand side of the gradient for the 
        /// <see cref="PaneHeader"/>
        /// </summary>
        /// <value>
        /// The color of the left-hand side of the gradient for the 
        /// <see cref="PaneHeader"/>
        /// </value>
        [Category("Appearance")]
        public Color PaneHeaderLeftColor
        {
            get
            {
                return paneTitleLabel.LeftColor;
            }
            set
            {
                paneTitleLabel.LeftColor= value;
                mHLCManuallySet = true;
            }
        }

        /// <summary>
        /// Gets or sets the color of the right-hand side of the gradient for the 
        /// <see cref="PaneHeader"/>
        /// </summary>
        /// <value>
        /// The color of the right-hand side of the gradient for the 
        /// <see cref="PaneHeader"/>
        /// </value>
        [Category("Appearance")]
        public Color PaneHeaderRightColor
        {
            get
            {
                return paneTitleLabel.RightColor;
            }
            set
            {
                paneTitleLabel.RightColor = value;
                mHRCManuallySet = true;
            }
        }

        /// <summary>
        /// Private boolean variable to record whether or not the
        /// <see cref="PropertyTree.PaneHeaderRightColor"/> has
        /// been manually set
        /// </summary>
        private bool mHRCManuallySet = false;
        
        /// <summary>
        /// Determines whether or not the designer should serialize the
        /// value of the <see cref="PropertyTree.PaneHeaderRightColor"/>
        /// property
        /// </summary>
        /// <returns>
        /// Returns <c>true</c> if the designer should serialize the value
        /// of <see cref="PropertyTree.PaneHeaderRightColor"/> to code, or
        /// <c>false</c> if it should not serialize the value to code.
        /// </returns>
        /// <remarks>
        /// The <c>ShouldSerialize[Property]</c> function is called by the
        /// WinForms designer to determine whether or not it should 
        /// serialize the property <c>[Property]</c>.   In this case, 
        /// <c>ShouldSerializePaneHeaderRightColor</c> returns true if
        /// <see cref="PropertyTree.PaneHeaderRightColor"/> has been 
        /// manually set during design-time, or false otherwise.
        /// </remarks>
        public bool ShouldSerializePaneHeaderRightColor()
        {
            return mHRCManuallySet;
        }  

        /// <summary>
        /// Private boolean variable to record whether or not the
        /// <see cref="PropertyTree.PaneHeaderRightColor"/> has
        /// been manually set
        /// </summary>
        private bool mHLCManuallySet = false;

        /// <summary>
        /// Determines whether or not the designer should serialize the
        /// value of the <see cref="PropertyTree.PaneHeaderLeftColor"/>
        /// property
        /// </summary>
        /// <returns>
        /// Returns <c>true</c> if the designer should serialize the value
        /// of <see cref="PropertyTree.PaneHeaderLeftColor"/> to code, or
        /// <c>false</c> if it should not serialize the value to code.
        /// </returns>
        /// <remarks>
        /// The <c>ShouldSerialize[Property]</c> function is called by the
        /// WinForms designer to determine whether or not it should 
        /// serialize the property <c>[Property]</c>.   In this case, 
        /// <c>ShouldSerializePaneHeaderLeftColor</c> returns true if
        /// <see cref="PropertyTree.PaneHeaderLeftColor"/> has been 
        /// manually set during design-time, or false otherwise.
        /// </remarks>
        public bool ShouldSerializePaneHeaderLeftColor()
        {
            return mHLCManuallySet;
        }  

        /// <summary>
        /// Resets the <see cref="PropertyTree.PaneHeaderRightColor"/> to its
        /// default value
        /// </summary>
        public void ResetPaneHeaderRightColor()
        {
            paneTitleLabel.RightColor = BackColor;
            mHRCManuallySet = false;
        }

        /// <summary>
        /// Resets the <see cref="PropertyTree.PaneHeaderLeftColor"/> to its
        /// default value
        /// </summary>
        public void ResetPaneHeaderLeftColor()
        {
            paneTitleLabel.LeftColor = SystemColors.ActiveCaption;
            mHRCManuallySet = false;
        }

        /// <summary>
        /// Responds to the event of our background color changing
        /// </summary>
        /// <param name="e">Information about the event</param>
        /// <remarks>
        /// If <see cref="PropertyTree.PaneHeaderRightColor"/> has not been 
        /// set manually, we change it to coincide with the new background
        /// color that was just set.
        /// </remarks>
        protected override void OnBackColorChanged(EventArgs e)
        {
            if(!mHRCManuallySet)
                paneTitleLabel.RightColor = BackColor;
        }

        /// <summary>
        /// Gets or sets a boolean value that indicates whether or not the
        /// <see cref="PaneHeader"/> is visible in the <c>PropertyTree</c>.
        /// </summary>
        /// <value>
        /// A boolean value that indicates whether or not the
        /// <see cref="PaneHeader"/> is visible in the <c>PropertyTree</c>.
        /// </value>
        [Category("Appearance")]
        [DefaultValue(true)]
        public bool PaneHeaderVisible
        {
            get
            {
                return paneTitleLabel.Visible;
            }
            set
            {
                paneTitleLabel.Visible = value;
            }
        }

        /// <summary>
        /// Gets or sets the width of the portion of the <c>PropertyTree</c>
        /// that houses the <see cref="TreeView"/>.
        /// </summary>
        /// <value>
        /// The width, in pixels, of the portion of the <c>PropertyTree</c> that
        /// houses the <see cref="TreeView"/>
        /// </value>
        [Category("Appearance")]	
        public int SplitterLeft
        {
            get { return splitter.Left; }
            set 
            { 
                tvTree.Width = value;
                splitter.Left = value; 
                AdjustPaneArea();
            }
        }

        /// <summary>
        /// Gets or sets the color of the <see cref="Splitter"/> that splits the <c>TreeView</c>
        /// and <c>PropertyPane</c> portions of the <c>PropertyTree</c>
        /// </summary>
        /// <value>
        /// The color of the <see cref="Splitter"/> that splits the <c>TreeView</c>
        /// and <c>PropertyPane</c> portions of the <c>PropertyTree</c>
        /// </value>
        [Category("Appearance")]
        public Color SplitterColor
        {
            get
            {
                return splitter.BackColor;
            }
            set
            {
                splitter.BackColor = value;
            }
        }

        /// <summary>
        /// Determines whether or not the designer should serialize the
        /// value of the <see cref="PropertyTree.SplitterColor"/>
        /// property
        /// </summary>
        /// <returns>
        /// Returns <c>true</c> if the designer should serialize the value
        /// of <see cref="PropertyTree.SplitterColor"/> to code, or
        /// <c>false</c> if it should not serialize the value to code.
        /// </returns>
        /// <remarks>
        /// The <c>ShouldSerialize[Property]</c> function is called by the
        /// WinForms designer to determine whether or not it should 
        /// serialize the property <c>[Property]</c>.   In this case, 
        /// <c>ShouldSerializeSplitterColor</c> returns true if
        /// <see cref="PropertyTree.SplitterColor"/> has been 
        /// manually set during design-time, or false otherwise.
        /// </remarks>
        public bool ShouldSerializeSplitterColor()
        {
            return splitter.BackColor != Color.DarkGray;
        }

        /// <summary>
        /// Resets the <see cref="PropertyTree.SplitterColor"/> to its
        /// default value
        /// </summary>
        public void ResetSplitterColor()
        {
            splitter.BackColor = Color.DarkGray;
        }
    }
}
